#include "Kuwahara.h"
#include<queue>
#include<cmath>
#include<android/log.h>

#define LOG_TAG "Kuwahara"
#define LOGV(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

struct pixel {
	double var;
	unsigned int pixelVal;
};
pixel pixelsCalculation(vector<int> arr, int count);
pixel getPixels(int pixels[], int width, int height, int startX, int startY,
		int endX, int endY);
void Rgb2Hsv(float R, float G, float B, float *H, float *S, float *V);
void Hsv2Rgb(float H, float S, float V, float *R, float *G, float *B);
vector<int> Roberts(vector<int> arr, int width, int height);
void contrastAndBrightness(int contrast, int bright, float *R, float *G,
		float *B);

vector<int> kuwahara(int pixels[], int width, int height, int count) {
	int vecLength = width * height;
	vector<int> retPixels(vecLength);
	if (count < 2) {
		for (int n = 0; n < vecLength; n++)
			retPixels[n] = pixels[n];
		return retPixels;
	}
	int startX = 0;
	int startY = 0;
	int endX = 0;
	int endY = 0;
	int pixelX = 0;
	int pixelY = 0;
	int temp = count - 1;
	pixel pixelval[4];

	float R, G, B;
	float H, S, V;

/*	int logPixel = 227 * width + 103;
	int xxx = pixels[logPixel];
	LOGV("R%d  G%d  B%d", xxx >> 16 & 0xFF, xxx >> 8 & 0xFF, xxx & 0xFF);
*/
	//------------------------------------------------------------------------------------------
	//----------------------------------------old method----------------------------------------
	/*	for (int i = 0; i < vecLength; i++) {

	 startX = (i - temp) % width;
	 if (startX < 0)
	 startX = 0;

	 startY = (i - temp * width) / width;
	 if (startY < 0)
	 startY = 0;

	 pixelX = i % width;
	 pixelY = i / width;

	 endX = (i + temp) % width;
	 if (endX < pixelX)
	 endX = width - 1;

	 endY = (i + temp * width) / width;
	 if (endY > height - 1)
	 endY = height - 1;

	 pixelval[0] = getPixels(pixels, width, height, startX, startY, pixelX,
	 pixelY);
	 pixelval[1] = getPixels(pixels, width, height, pixelX, startY, endX,
	 pixelY);
	 pixelval[2] = getPixels(pixels, width, height, startX, pixelY, pixelX,
	 endY);
	 pixelval[3] = getPixels(pixels, width, height, pixelX, pixelY, endX,
	 endY);

	 for (int j = 0; j < 3; j++) {
	 if (pixelval[j].var < pixelval[j + 1].var)
	 pixelval[j + 1] = pixelval[j];
	 }
	 retPixels[i] = pixelval[3].pixelVal;
	 }*/
	//----------------------------------------------------------------------------------------
	int i = 0;
	int num = 0;
	queue<pixel> varQueue;
	pixel minVarPixels;

	for (int iY = 0; iY < temp; iY++) {
		for (int iX = 0; iX < temp; iX++) {
			i = iY * width + iX;
			varQueue.push(getPixels(pixels, width, height, 0, 0, iX, iY));
			endY = (iY + temp < height) ? (iY + temp) : (height - 1);
			startX = iX;
			while (startX < width) {
				if (startX + temp < width)
					varQueue.push(
							getPixels(pixels, width, height, startX, 0,
									startX + temp, iY));
				else
					varQueue.push(
							getPixels(pixels, width, height, startX, 0,
									width - 1, iY));

				startX += temp;
			}
			varQueue.push(getPixels(pixels, width, height, 0, iY, iX, endY));

			num = 1;
			while (i < vecLength) {

				pixelX = i % width;
				pixelY = i / width;

				endX = pixelX + temp;
				if (endX > width - 1)
					endX = width - 1;

				pixelval[3] = getPixels(pixels, width, height, pixelX, pixelY,
						endX, endY);

				pixelval[0] = varQueue.front();
				varQueue.pop();
				pixelval[1] = varQueue.front();
				pixelval[2] = varQueue.back();

				varQueue.push(pixelval[3]);

				minVarPixels = pixelval[0];
				for (int j = 1; j <= 3; j++)
					if (minVarPixels.var > pixelval[j].var)
						minVarPixels = pixelval[j];

				retPixels[i] = minVarPixels.pixelVal;
//**********************cartoon***************************

				R = retPixels[i] >> 16 & 0xFF;
				G = retPixels[i] >> 8 & 0xFF;
				B = retPixels[i] & 0xFF;
				Rgb2Hsv(R, G, B, &H, &S, &V);
				Hsv2Rgb(H, S + 0.1, V + 0.1, &R, &G, &B);
				//contrastAndBrightness(1, 60, &R, &G, &B);
				retPixels[i] = 0xFF << 24 | ((int) R << 16 & 0xFF0000)
						| ((int) G << 8 & 0xFF00) | ((int) B & 0xFF);
//********************************************************
				i += temp;
				if (i % width < pixelX) {
					i = (iY + temp * num) * width + iX;
					if (i < vecLength) {
						num++;
						varQueue.pop();
						pixelY = endY;
						endY = pixelY + temp;
						if (endY > height - 1)
							endY = height - 1;
						varQueue.push(
								getPixels(pixels, width, height, 0, pixelY, iX,
										endY));
					} else {
						while (!varQueue.empty())
							varQueue.pop();
					}
				}
			}

		}
	}
	//------------------------------------------------------------------------------------------

/*	int yyy = retPixels[logPixel];
	LOGV("R%d  G%d  B%d", yyy >> 16 & 0xFF, yyy >> 8 & 0xFF, yyy & 0xFF);
*/
	//*************cartoon***draw edge**************
	retPixels = Roberts(retPixels, width, height);

	return retPixels;
}

pixel getPixels(int pixels[], int width, int height, int startX, int startY,
		int endX, int endY) {
	int count = 0;
	count = (endX - startX + 1) * (endY - startY + 1);
	pixel pixelcal;

	if (count < 0) {
		pixelcal.pixelVal = pixels[0];
		pixelcal.var = 0;
		return pixelcal;
	}

	vector<int> buffPixels(count);

	for (int j = 0; j <= endY - startY; j++)
		for (int k = 0; k <= endX - startX; k++)
			buffPixels[j * (endX - startX + 1) + k] = pixels[(startY + j)
					* width + startX + k];

	pixelcal = pixelsCalculation(buffPixels, count);

	return pixelcal;
}

pixel pixelsCalculation(vector<int> arr, int count) {
	pixel pixelval;
	if (count < 0) {
		pixelval.pixelVal = arr[0];
		pixelval.var = 0;
		return pixelval;
	}
	double sumR = 0;
	double sumG = 0;
	double sumB = 0;
	double averageR = 0;
	double averageG = 0;
	double averageB = 0;
	double varR = 0;
	double varG = 0;
	double varB = 0;
	double sqrR = 0;
	double sqrG = 0;
	double sqrB = 0;
	vector<int> R(count);
	vector<int> G(count);
	vector<int> B(count);

	for (int i = 0; i < count; i++) {
		R[i] = arr[i] >> 16 & 0xFF;
		G[i] = arr[i] >> 8 & 0xFF;
		B[i] = arr[i] & 0xFF;
		sumR += R[i];
		sumG += G[i];
		sumB += B[i];
		sqrR += R[i] * R[i];
		sqrG += G[i] * G[i];
		sqrB += B[i] * B[i];
	}
	averageR = sumR / count;
	averageG = sumG / count;
	averageB = sumB / count;

	varR = sqrR - sumR * averageR;
	varG = sqrG - sumG * averageG;
	varB = sqrB - sumB * averageB;

	pixelval.var = (varR + varG + varB) / count;
	pixelval.pixelVal = 0xFF << 24 | ((int) averageR << 16 & 0xFF0000)
			| ((int) averageG << 8 & 0xFF00) | ((int) averageB & 0xFF);

	return pixelval;
}

void Rgb2Hsv(float R, float G, float B, float *H, float *S, float *V) {
	// r,g,b values are from 0 to 255
	// h = [0,360], s = [0,1], v = [0,1]
	// if s == 0, then h = -1 (undefined)

	float min, max, delta, tmp;
	tmp = (R < G) ? R : G;
	min = (tmp < B) ? tmp : B;

	tmp = (R > G) ? R : G;
	max = (tmp > B) ? tmp : B;

	*V = max / 255; // v

	delta = max - min;

	if (max != 0)
		*S = delta / max; // s
	else {
		// r = g = b = 0 // s = 0, v is undefined
		*S = 0;
		*H = 0;
		return;
	}
	if (delta == 0)
		*H = 0;
	else if (R == max)
		*H = (G - B) / delta; // between yellow & magenta
	else if (G == max)
		*H = 2 + (B - R) / delta; // between cyan & yellow
	else
		*H = 4 + (R - G) / delta; // between magenta & cyan

	*H *= 60; // degrees
	if (H < 0)
		*H += 360;
}

void Hsv2Rgb(float H, float S, float V, float *R, float *G, float *B) {
	int i;
	float f, p, q, t;
	if (S > 1)
		S = 1;
	if (V > 1)
		V = 1;
	V *= 255;
	if (S == 0) {
		// achromatic (grey)
		*R = *G = *B = V;
		return;
	}

	H /= 60; // sector 0 to 5
	i = floor(H);
	f = H - i; // factorial part of h

	p = V * (1 - S);
	q = V * (1 - S * f);
	t = V * (1 - S * (1 - f));

	switch (i) {
	case 0:
		*R = V;
		*G = t;
		*B = p;
		break;
	case 1:
		*R = q;
		*G = V;
		*B = p;
		break;
	case 2:
		*R = p;
		*G = V;
		*B = t;
		break;
	case 3:
		*R = p;
		*G = q;
		*B = V;
		break;
	case 4:
		*R = t;
		*G = p;
		*B = V;
		break;
	default: // case 5:
		*R = V;
		*G = p;
		*B = q;
		break;
	}
}

vector<int> Roberts(vector<int> arr, int width, int height) {
	vector<int> tempVec(width * height);
	int tempPixel = 0;
	int pixel[4];
	float R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
	int tempR, tempG, tempB;
	for (int iY = 0; iY < height; iY++) {
		for (int iX = 0; iX < width; iX++) {
			if (iY != height - 1 && iX != width - 1) {
				pixel[0] = arr[iY * width + iX];
				pixel[1] = arr[iY * width + iX + 1];
				pixel[2] = arr[(iY + 1) * width + iX];
				pixel[3] = arr[(iY + 1) * width + iX + 1];
				R0 = pixel[0] >> 16 & 0xFF;
				R1 = pixel[1] >> 16 & 0xFF;
				R2 = pixel[2] >> 16 & 0xFF;
				R3 = pixel[3] >> 16 & 0xFF;
				G0 = pixel[0] >> 8 & 0xFF;
				G1 = pixel[1] >> 8 & 0xFF;
				G2 = pixel[2] >> 8 & 0xFF;
				G3 = pixel[3] >> 8 & 0xFF;
				B0 = pixel[0] & 0xFF;
				B1 = pixel[1] & 0xFF;
				B2 = pixel[2] & 0xFF;
				B3 = pixel[3] & 0xFF;
				tempR = (int) sqrt(
						(R0 - R3) * (R0 - R3) + (R1 - R2) * (R1 - R2));
				tempG = (int) sqrt(
						(G0 - G3) * (G0 - G3) + (G1 - G2) * (G1 - G2));
				tempB = (int) sqrt(
						(B0 - B3) * (B0 - B3) + (B1 - B2) * (B1 - B2));
				tempPixel = 0xFF << 24 | (tempR << 16 & 0xFF0000)
						| (tempG << 8 & 0xFF00) | (tempB & 0xFF);
				if (tempR > 15 || tempG > 15 || tempB > 15)
					tempVec[iY * width + iX] = 0;
				else
					tempVec[iY * width + iX] = arr[iY * width + iX];
			} else {
				tempVec[iY * width + iX] = arr[iY * width + iX];
			}
		}
	}
	return tempVec;
}

void contrastAndBrightness(int contrast, int bright, float *R, float *G,
		float *B) {

	*R = *R * contrast + bright;
	if (*R > 255)
		*R = 255;

	*G = *G * contrast + bright;
	if (*G > 255)
		*G = 255;

	*B = *B * contrast + bright;
	if (*B > 255)
		*B = 255;
}
